#include "BaseUnit.h"
#include "Level.h"
#include "MyGame.h"
#include "Physics.h"

BaseUnit::BaseUnit()
{
    init();
}

BaseUnit::BaseUnit(const Vector2df p, const Vector2df v)
{
    init();
    setPosition(p);
    m_velocity = v;
    m_maxSpeed = m_velocity;
    m_direction = NumberUtility::sign(v.x);
}

BaseUnit::~BaseUnit()
{
    // no need to delete anything here as all pointer data is shared and will either
    // be deleted by the level class or one of the caches
}

void BaseUnit::init()
{
    m_parent = NULL;
    m_child = NULL;
    m_level = NULL;
    setPosition(Vector2df(0.0,0.0));
    m_velocity = Vector2df(0.0,0.0);
    m_maxSpeed = m_velocity;
    m_size = Vector2df(-1,-1);

    m_counter.init(100,MILLI_SECONDS,this,BaseUnit::countDownWrapper);
    m_counter.setRewind(STOP);
    m_acceleration[0] = Vector2df(0.0,0.0);
    m_acceleration[1] = Vector2df(0.0,0.0);
    m_shouldBeRemoved = false;
    m_spawnData = NULL;
    m_state = usNONE;
}

//----------------------
// public declarations
//----------------------

int BaseUnit::getHeight() const
{
    if (m_sprite)
    {
        return m_sprite->getHeight();
    }
    else
    {
        return m_size.y;
    }
}

int BaseUnit::getWidth() const
{
    if (m_sprite)
    {
        return m_sprite->getWidth();
    }
    else
    {
        return m_size.x;
    }
}

Vector2df BaseUnit::getPosition(bool inTiles) const
{
    if (inTiles)
    {
        return Vector2df(floor(m_position.x / getLevel()->GetTileSize()),floor(m_position.y / getLevel()->GetTileSize()));
    }
    else
    {
        return m_position;
    }
}

Vector2df BaseUnit::getScreenPosition() const
{
    if (m_sprite)
    {
        return m_sprite->getPosition();
    }
    else
    {
        return Vector2df(-1,-1);
    }
}

Vector2df BaseUnit::getSize() const
{
    if (m_sprite)
    {
        return Vector2df(m_sprite->getWidth(),m_sprite->getHeight());
    }
    else
    {
        return m_size;
    }
}

Vector2df BaseUnit::getSpeed() const
{
    if (m_parent)
    {
        return m_parent->getSpeed();
    }
    else
    {
        return m_velocity;
    }
}

Vector2df BaseUnit::getCenter() const
{
    return (getPosition() + getSize() / 2.0f);
}

Vector2df BaseUnit::getScreenCenter() const
{
    return (getScreenPosition() + getSize() / 2.0f);
}


void BaseUnit::accelerate(const Vector2df& value, const Vector2df& max)
{
    m_acceleration[0] = value;
    m_acceleration[1] = max;
}

void BaseUnit::accelerateX(CRfloat val, CRfloat max)
{
    m_acceleration[0].x = val;
    m_acceleration[1].x = max;
}

void BaseUnit::accelerateY(CRfloat val, CRfloat max)
{
    m_acceleration[0].y = val;
    m_acceleration[1].y = max;
}

void BaseUnit::stopRightThere()
{
    m_velocity = Vector2df(0,0);
    accelerate(Vector2df(0,0),Vector2df(0,0));
}


void BaseUnit::render()
{
    if (m_sprite) m_sprite->render();
}

void BaseUnit::updateScreenPosition(const Vector2df mapOffset)
{
    if (m_sprite)
    {
        m_sprite->setPosition(getPosition() - mapOffset);
    }
}

void BaseUnit::update()
{
    m_counter.update();
    if (m_sprite) m_sprite->update();
    if (m_parent)
    {
        if (m_parent->getDirection() == 1)
            setPosition(Vector2df(m_parent->getPosition().x + 12,m_parent->getPosition().y - getHeight() - 2));
        else
            setPosition(Vector2df(m_parent->getPosition().x  - 18,m_parent->getPosition().y - getHeight() - 2));
    }
    else
    {
        // Acceleration stuff
        if (m_velocity.x == m_acceleration[1].x)
        {
            m_acceleration[0].x = 0;
            m_acceleration[1].x = 0;
        }
        if (m_velocity.y == m_acceleration[1].y)
        {
            m_acceleration[0].y = 0;
            m_acceleration[1].y = 0;
        }
        if (m_acceleration[0].x > 0)
        {
            m_velocity.x += min(max(m_acceleration[1].x-m_velocity.x,(float)0.0),m_acceleration[0].x);
        }
        else if (m_acceleration[0].x < 0)
        {
            m_velocity.x += max(min(m_acceleration[1].x-m_velocity.x,(float)0.0),m_acceleration[0].x);
        }
        if (m_acceleration[0].y > 0)
        {
            m_velocity.y += min(max(m_acceleration[1].y-m_velocity.y,(float)0.0),m_acceleration[0].y);
        }
        else if (m_acceleration[0].y < 0)
        {
            m_velocity.y += max(min(m_acceleration[1].y-m_velocity.y,(float)0.0),m_acceleration[0].y);
        }
    }
}


// This should be overridden in sub-classes to trigger an ending animation, and only at the end of that animation, or after a Countdown
// actually remove the object from the level
void BaseUnit::remove()
{
    m_shouldBeRemoved = true;
};


void BaseUnit::resetParent()
{
    BaseUnit *temp = m_parent;
    m_parent = NULL;
    if (temp && temp->isCarryingChild()) temp->resetChild();
}

void BaseUnit::setParent(BaseUnit *parent)
{
    if (m_parent == parent) return; // don't loop
    if (m_parent)
    {
        resetParent();
    }
    m_parent = parent;
    // TODO: Move the pick-up code (position change to Hero class)
    if (m_parent)
    {
        m_parent->setChild(this);
        m_counter.stop();
        m_velocity = Vector2df(0,0);
        m_acceleration[0] = Vector2df(0,0);
        if (m_parent->getDirection() == 1)
            setPosition(Vector2df(m_parent->getPosition().x + 12,m_parent->getPosition().y - getHeight() - 8));
        else
            setPosition(Vector2df(m_parent->getPosition().x - 18,m_parent->getPosition().y - getHeight() - 8));
    }
};

void BaseUnit::resetChild()
{
    BaseUnit *temp = m_child;
    m_child = NULL;
    if (temp && temp->isBeingCarried())
        temp->resetParent();
}

void BaseUnit::setChild(BaseUnit *child)
{
    if (m_child == child) return; // don't loop
    if (m_child)
    {
        // We already have a child, do something here?
        resetChild();
    }
    m_child = child;
    if (m_child)
    {
        m_child->setParent(this);
    }
};


void BaseUnit::action(BaseUnit* unit)
{
    //
}
